# -*- encoding:utf-8 -*-

import sys
import os
from PyQt4 import QtGui, QtCore, Qt
from functools import partial
import numpy as np
import cv2
import json
import logging
from myicons import MyIcons
from enum import Enum

logging.basicConfig(level=logging.DEBUG)

tableChanged = QtCore.pyqtSignal()


class RightsideButtonBar(QtGui.QWidget):
    def __init__(self, widgets=[], parent=None):
        super().__init__(parent=parent)
        self.hbox = QtGui.QHBoxLayout()
        self.setLayout(self.hbox)
        self.hbox.addStretch(1)
        self._addWidgets(widgets)

    def _addWidgets(self, widgets):
        for widget in widgets:
            self.hbox.addWidget(widget)


class ImageShow(QtGui.QLabel):
    class PickMode(Enum):
        no_pick = 0
        pick_block = 1
        pick_num_pos = 2
        pick_long_block = 3

    def __init__(self, parent=None, grandparent=None):
        super().__init__(parent=parent)
        self.pick_point = False
        self.grandparent = grandparent
        self.pick_mode = self.PickMode.no_pick

    def pick_on(self):
        logging.debug("pick on")
        self.pick_point = True

    def pick_off(self):
        logging.debug("pick off")
        self.pick_point = False

    def mousePressEvent(self, event):
        if self.pick_point:
            x = event.x()
            y = event.y()
            if self.pick_mode == self.PickMode.pick_block:
                self.grandparent.add_block(x, y)
            self.pick_off()

    def keyReleaseEvent(self, event):
        logging.debug("key pressed")
        if self.pick_point and event.key() == QtCore.Qt.Key_Escape:
            logging.debug("quit pick mode")
            self.pick_off()
            self.pick_mode = self.PickMode.no_pick
            if self.grandparent and isinstance(self.grandparent, QtGui.QMainWindow):
                self.grandparent.statusBar().clear()


class TemplateManagerMain(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.image = None
        self.image_origin = None
        self.template = None
        self.myicons = MyIcons()
        self.init_ui()
        self.add_actions()
        self.connect_buttons()

    def init_ui(self):
        # 用于替代MainWindow中的CenterWidget
        center = QtGui.QWidget()
        hbox = QtGui.QHBoxLayout()

        image_vbox = QtGui.QVBoxLayout()
        # self.image_show = QtGui.QLabel("")
        self.image_show = ImageShow(grandparent=self)

        empty = QtGui.QPixmap(800, 600)
        empty.fill()
        self.image_show.setPixmap(empty)

        image_vbox.addWidget(self.image_show)
        image_vbox.addStretch(1)
        hbox.addLayout(image_vbox)

        # 右侧面板
        grid = QtGui.QGridLayout()
        grid.setSpacing(10)

        # 图片/模板载入
        # from row 0
        row = 0
        self.image_finder = PathFinder(hint="选择图片", filter="图片(*.png; *.jpg; *.jpeg)")
        grid.addWidget(self.image_finder, row, 0, 1, 4)
        row += 1

        self.template_finder = PathFinder(hint="选择模板", filter="模版(*.json)")
        grid.addWidget(self.template_finder, row, 0, 1, 4)
        row += 1

        grid.addWidget(QtGui.QLabel("图片信息:"), row, 0, 1, 2)
        row += 1
        grid.addWidget(QtGui.QLabel("宽:"), row, 0)
        grid.addWidget(QtGui.QLabel("高:"), row, 2)
        self.image_width = QtGui.QSpinBox()
        self.image_height = QtGui.QSpinBox()
        grid.addWidget(self.image_width, 3, 1)
        grid.addWidget(self.image_height, 3, 3)
        row += 1

        grid.addWidget(QtGui.QLabel("块信息:"), row, 0, 1, 2)
        row += 1
        grid.addWidget(QtGui.QLabel("宽:"), row, 0)
        grid.addWidget(QtGui.QLabel("高:"), row, 2)
        self.block_width = QtGui.QSpinBox()
        self.block_height = QtGui.QSpinBox()
        grid.addWidget(self.block_width, row, 1)
        grid.addWidget(self.block_height, row, 3)
        row += 1
        grid.addWidget(QtGui.QLabel("列数:"), row, 0)
        self.num_cols = QtGui.QSpinBox()
        grid.addWidget(self.num_cols, row, 1)
        row += 1

        grid.addWidget(QtGui.QLabel("块列表:"), row, 0, 1, 2)
        self.block_move_up = QtGui.QPushButton()
        self.block_move_up.setIcon(self.myicons.icon_up)
        self.block_move_up.setToolTip("上移")
        self.block_move_down = QtGui.QPushButton()
        self.block_move_down.setIcon(self.myicons.icon_down)
        self.block_move_down.setToolTip("下移")
        self.block_add = QtGui.QPushButton()
        self.block_add.setIcon(self.myicons.icon_add)
        self.block_add.setToolTip("添加")
        self.block_remove = QtGui.QPushButton()
        self.block_remove.setIcon(self.myicons.icon_remove)
        self.block_remove.setToolTip("删除")

        button_list = [self.block_move_up, self.block_move_down, self.block_add, self.block_remove]
        blocks_hbox = RightsideButtonBar(widgets=button_list)

        grid.addWidget(blocks_hbox, row, 1, 1, 3)
        row += 1
        self.block_list = QtGui.QTableWidget()
        grid.addWidget(self.block_list, row, 0, 5, 4)
        row += 5

        grid.addWidget(QtGui.QLabel("答案映射:"), row, 0, 1, 2)
        row += 1
        col = 0
        self.answer_map = []
        for i, answer in enumerate("ABCD"):
            col = i * 2
            if col == 4:
                row += 1
            col %= 4
            grid.addWidget(QtGui.QLabel(answer + ":"), row, col)
            a_map = QtGui.QSpinBox()
            self.answer_map.append(a_map)
            grid.addWidget(a_map, row, col + 1)
        row += 1

        grid.addWidget(QtGui.QLabel("标记:"), row, 0, 1, 2)
        row += 1
        grid.addWidget(QtGui.QLabel("宽:"), row, 0)
        self.mark_width = QtGui.QSpinBox()
        grid.addWidget(self.mark_width, row, 1)
        grid.addWidget(QtGui.QLabel("高:"), row, 2)
        self.mark_height = QtGui.QSpinBox()
        grid.addWidget(self.mark_height, row, 3)
        row += 1

        # 特殊块
        # 包括学号块，多选块
        grid.addWidget(QtGui.QLabel("学号区域:"), row, 0, 1, 2)
        row += 1
        grid.addWidget(QtGui.QLabel("宽:"), row, 0)
        self.num_width = QtGui.QSpinBox()
        grid.addWidget(self.num_width, row, 1)
        grid.addWidget(QtGui.QLabel("高:"), row, 2)
        self.num_height = QtGui.QSpinBox()
        grid.addWidget(self.num_height, row, 3)
        row += 1
        grid.addWidget(QtGui.QLabel("位数:"), row, 0)
        self.num_digits = QtGui.QSpinBox()
        grid.addWidget(self.num_digits, row, 1)
        row += 1
        grid.addWidget(QtGui.QLabel("学号位置:"), row, 0, 1, 2)
        self.pick_num_pos = QtGui.QPushButton()
        self.pick_num_pos.setIcon(self.myicons.icon_options)
        self.pick_num_pos.setToolTip("选点")
        grid.addWidget(RightsideButtonBar(widgets=[self.pick_num_pos]), row, 1, 1, 3)
        row += 1
        grid.addWidget(QtGui.QLabel("x:"), row, 0)
        self.num_x = QtGui.QSpinBox()
        grid.addWidget(self.num_x, row, 1)
        grid.addWidget(QtGui.QLabel("y:"), row, 2)
        self.num_y = QtGui.QSpinBox()
        grid.addWidget(self.num_y, row, 3)
        row += 1
        grid.addWidget(QtGui.QLabel("映射关系:"), row, 0, 1, 2)
        row += 1
        col = 0
        self.num_map = []
        for i, answer in enumerate("0123456789"):
            col = i * 2
            if col > 0 and col % 4 == 0:
                row += 1
            col %= 4
            grid.addWidget(QtGui.QLabel(answer + ":"), row, col)
            a_map = QtGui.QSpinBox()
            self.num_map.append(a_map)
            grid.addWidget(a_map, row, col + 1)
        row += 1
        # set maximun nummber to a relatively large one
        entrys = [self.image_width, self.image_height, self.block_width, self.block_height, self.num_cols,
                  self.mark_height, self.mark_width]
        entrys.extend(self.answer_map)
        num_stuff = [self.num_width, self.num_height, self.num_digits, self.num_x, self.num_y]
        entrys.extend(num_stuff)
        entrys.extend(self.num_map)
        self.spinboxs = entrys
        for entry in entrys:
            entry.setMaximum(100000)

        vbox = QtGui.QVBoxLayout()
        vbox.addLayout(grid)
        vbox.addStretch(1)

        # add scroll bar to the pannel
        pannelwidget = QtGui.QWidget()
        pannelwidget.setLayout(vbox)
        scrollpannel = QtGui.QScrollArea()
        scrollpannel.setWidget(pannelwidget)
        scrollpannel.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        scrollpannel.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        scrollpannel.setWidgetResizable(True)

        hbox.addWidget(scrollpannel)
        ## hbox.addLayout(vbox)
        # hbox.addStretch(1)
        center.setLayout(hbox)
        self.setCentralWidget(center)
        self.statusBar()

    def add_actions(self):
        toolbar = self.addToolBar('Main')
        load_image = QtGui.QAction('载入图片', self)
        load_image.setIcon(self.myicons.icon_open_image)
        self.connect(load_image, QtCore.SIGNAL('triggered()'), self.open_image)
        toolbar.addAction(load_image)

        load_template = QtGui.QAction('载入模版', self)
        load_template.setIcon(self.myicons.icon_open)
        self.connect(load_template, QtCore.SIGNAL('triggered()'), partial(self.open_template, forced=True))
        toolbar.addAction(load_template)

        save_template = QtGui.QAction('保存模版', self)
        save_template.setIcon(self.myicons.icon_save)
        self.connect(save_template, QtCore.SIGNAL('triggered()'), self.save_template)
        toolbar.addAction(save_template)

    def connect_buttons(self):
        self.connect(self.image_finder.load, QtCore.SIGNAL('clicked()'),
                     self.open_image)
        self.connect(self.template_finder.load, QtCore.SIGNAL('clicked()'),
                     self.open_template)

        # 块列表处理
        self.connect(self.block_move_up, QtCore.SIGNAL('clicked()'),
                     self.move_up_block)
        self.connect(self.block_move_down, QtCore.SIGNAL('clicked()'),
                     self.move_down_block)
        self.connect(self.block_add, QtCore.SIGNAL('clicked()'),
                     self.pick_block)
        self.connect(self.block_remove, QtCore.SIGNAL('clicked()'),
                     self.remove_block)

    def connect_template_info(self):
        entrys = self.spinboxs
        for entry in entrys:
            self.connect(entry, QtCore.SIGNAL('valueChanged(int)'), self.update_template)
        self.connect(self.block_list, QtCore.SIGNAL('cellChanged(int, int)'), self.update_template)
        self.connect(self.block_list, QtCore.SIGNAL('tableChanged()'), self.update_template)

    def disconnect_template_info(self):
        entrys = self.spinboxs
        for entry in entrys:
            self.disconnect(entry, QtCore.SIGNAL('valueChanged(int)'), self.update_template)
        self.disconnect(self.block_list, QtCore.SIGNAL('cellChanged(int, int)'), self.update_template)
        self.disconnect(self.block_list, QtCore.SIGNAL('tableChanged()'), self.update_template)

    def open_image(self):
        path = self.image_finder.path
        if not path:
            path = self.image_finder.find_file()
        if path is not None:
            self.show_image(path)
            if self.template:
                self.show_template()

    def show_image(self, image_path):
        # read as grayscale image
        cv2image = cv2.imread(image_path, 0)
        assert (isinstance(cv2image, np.ndarray))
        if cv2image.ndim == 3:
            h, w, d = cv2image.shape
            image_format = QtGui.QImage.Format_RGB888
        else:
            h, w = cv2image.shape
            d = 1
            image_format = QtGui.QImage.Format_Indexed8

        image = QtGui.QImage(cv2image.tostring(), w, h, w * d, image_format)
        pixmap = QtGui.QPixmap(image)
        self.image_show.setPixmap(pixmap)
        self.image = pixmap
        self.image_origin = QtGui.QPixmap(pixmap)

    def open_template(self, forced=False):
        path = self.template_finder.path
        if not path or forced:
            path = self.template_finder.find_file()
        if path is not None:
            self.read_template(path)
            self.disconnect_template_info()
            self.show_template()
            self.connect_template_info()

    def read_template(self, path):
        with open(path, 'r') as jsonfile:
            self.template = json.load(jsonfile)

    def show_template(self):
        template = self.template
        size = template['size']
        self.image_width.setValue(size['w'])
        self.image_height.setValue(size['h'])

        block_info = template['block_info']
        self.block_width.setValue(block_info['w'])
        self.block_height.setValue(block_info['h'])
        self.num_cols.setValue(block_info['n'])

        blocks = template['blocks']
        self.block_list.clear()
        self.block_list.setRowCount(len(blocks))
        self.block_list.setColumnCount(2)
        self.block_list.setHorizontalHeaderLabels(['x', 'y'])
        for row, left_top in enumerate(blocks):
            x, y = left_top
            self.block_list.setItem(row, 0, QtGui.QTableWidgetItem(str(x)))
            self.block_list.setItem(row, 1, QtGui.QTableWidgetItem(str(y)))

        answer_map = template['answer_map']
        for i, answer in enumerate('ABCD'):
            self.answer_map[i].setValue(answer_map[answer])

        mark = template['mark']
        self.mark_width.setValue(mark['w'])
        self.mark_height.setValue(mark['h'])

        if 'student_num' in template:
            # for older template student num in not implemented
            student_num = template['student_num']
            self.num_width.setValue(student_num['w'])
            self.num_height.setValue(student_num['h'])
            self.num_digits.setValue(student_num['n'])
            self.num_x.setValue(student_num['x'])
            self.num_y.setValue(student_num['y'])
            num_map = student_num['num_map']
            for i, d in enumerate('0123456789'):
                self.num_map[i].setValue(num_map[d])
        else:
            self.update_template()

        self.draw_template()

    def draw_template(self):
        template = self.template
        size = template['size']
        if self.image_origin:
            self.image = QtGui.QPixmap(self.image_origin)
        else:
            self.image = QtGui.QPixmap(size['w'], size['h'])
            self.image.fill()
        qp = QtGui.QPainter(self.image)
        if template:
            qp.setPen(Qt.QColor(0, 0, 0, 255))
            qp.drawRect(0, 0, size['w'], size['h'])
            blocks = template['blocks']
            for i, block in enumerate(blocks):
                self.draw_block(qp, block, i + 1)
            self.draw_student_num(qp)
        self.image_show.setPixmap(self.image)
        self.image_show.setPixmap(self.image)

    def draw_student_num(self, qpainter):
        template = self.template
        student_num = template['student_num']
        w = student_num['w']
        h = student_num['h']
        n = student_num['n']
        x = student_num['x']
        y = student_num['y']
        num_map = student_num['num_map']

        # colors
        red = Qt.QColor(255, 0, 0, 255)
        shallow = Qt.QColor(255, 0, 255, 150)
        blue = Qt.QColor(0, 0, 255, 255)
        green = Qt.QColor(0, 255, 0, 255)
        yellow = Qt.QColor(255, 255, 0, 255)
        transparent = Qt.QColor('transparent')

        # draw border
        qpainter.setPen(red)
        qpainter.drawRect(x, y, w, h)

        mark_height = template['mark']['h']
        # draw columns
        if n > 0:
            col_inc = w / n
            for col in range(1, n):
                col_x = x + col * col_inc
                qpainter.drawLine(col_x, y, col_x, y + h)
            for col in range(n):
                # draw every mark
                col_x = x + col * col_inc
                for i, answer in enumerate('0123456789'):
                    qpainter.setPen(transparent)
                    if i % 2 == 0:
                        alpha = yellow.alpha()
                        yellow.setAlpha(100)
                        qpainter.setBrush(yellow)
                        yellow.setAlpha(alpha)
                    else:
                        alpha = green.alpha()
                        green.setAlpha(100)
                        qpainter.setBrush(green)
                        yellow.setAlpha(alpha)
                    mark_y = y + num_map[answer]
                    qpainter.drawRect(col_x, mark_y, col_inc, mark_height * 1.6)
        qpainter.setBrush(transparent)

    def draw_block(self, qpainter, block, idx=None):
        logging.debug("draw block #{}".format(idx))
        template = self.template
        block_info = template['block_info']
        w, h = block_info['w'], block_info['h']
        n = block_info['n']
        x, y = block

        red = Qt.QColor(255, 0, 0, 255)
        shallow = Qt.QColor(255, 0, 255, 150)
        blue = Qt.QColor(0, 0, 255, 255)
        green = Qt.QColor(0, 255, 0, 255)
        yellow = Qt.QColor(255, 255, 0, 255)
        transparent = Qt.QColor('transparent')

        # draw border
        qpainter.setPen(red)
        qpainter.drawRect(x, y, w, h)

        answer_map = template['answer_map']
        mark_height = template['mark']['h']
        # draw columns
        col_inc = w / n
        for col in range(1, n):
            col_x = x + col * col_inc
            qpainter.drawLine(col_x, y, col_x, y + h)
        for col in range(n):
            # draw every mark
            col_x = x + col * col_inc
            for i, answer in enumerate('ABCD'):
                qpainter.setPen(transparent)
                if i % 2 == 0:
                    alpha = yellow.alpha()
                    yellow.setAlpha(100)
                    qpainter.setBrush(yellow)
                    yellow.setAlpha(alpha)
                else:
                    alpha = green.alpha()
                    green.setAlpha(100)
                    qpainter.setBrush(green)
                    yellow.setAlpha(alpha)
                mark_y = y + answer_map[answer]
                qpainter.drawRect(col_x, mark_y, col_inc, mark_height * 1.6)
        qpainter.setBrush(transparent)

        # draw number
        font = Qt.QFont()
        font.setPixelSize(h / 5 * 4)
        qpainter.setPen(shallow)
        qpainter.setFont(font)
        font_metrics = Qt.QFontMetrics(font)
        text_width = font_metrics.width(str(idx))
        tx = x + w / 2 - text_width / 2
        ty = y + font_metrics.height()
        qpainter.drawText(tx, ty, str(idx))

    def update_template(self):
        logging.debug("update template")
        template = self.template
        size = template['size']
        size['w'] = self.image_width.value()
        size['h'] = self.image_height.value()

        block_info = template['block_info']
        block_info['w'] = self.block_width.value()
        block_info['h'] = self.block_height.value()
        block_info['n'] = self.num_cols.value()

        blocks = []
        for row in range(self.block_list.rowCount()):
            x = int(self.block_list.item(row, 0).text())
            y = int(self.block_list.item(row, 1).text())
            blocks.append([x, y])
        template['blocks'] = blocks

        mark = template['mark']
        mark['w'] = self.mark_width.value()
        mark['h'] = self.mark_height.value()

        answer_map = template['answer_map']
        for i, answer in enumerate('ABCD'):
            answer_map[answer] = self.answer_map[i].value()

        if 'student_num' not in template:
            template['student_num'] = {}
        student_num = template['student_num']
        student_num['w'] = self.num_width.value()
        student_num['h'] = self.num_height.value()
        student_num['n'] = self.num_digits.value()
        student_num['x'] = self.num_x.value()
        student_num['y'] = self.num_y.value()
        num_map = {}
        for i, answer in enumerate('0123456789'):
            num_map[answer] = self.num_map[i].value()
        student_num['num_map'] = num_map

        self.draw_template()

    def save_template(self):
        if self.template:
            orginal_path = self.template_finder.path
            if orginal_path:
                folder, filename = os.path.split(orginal_path)
                if not os.path.isdir(folder):
                    orginal_path = None
            filepath = QtGui.QFileDialog.getSaveFileName(directory=orginal_path, filter='*.json')
            if filepath:
                with open(filepath, 'w') as jsonfile:
                    json.dump(self.template, jsonfile)

    def remove_block(self):
        if not self.template:
            logging.debug("template not loaded")
            return
        block_list = self.block_list
        idx = block_list.selectedIndexes()
        if idx:
            row = idx[0].row()
            logging.debug('remove row #%d' % row)
            block_list.removeRow(row)
            block_list.emit(QtCore.SIGNAL('tableChanged()'))

    def pick_block(self, x=0, y=0):
        self.image_show.pick_on()
        self.image_show.setFocus()
        self.image_show.pick_mode = ImageShow.PickMode.pick_block
        self.statusBar().showMessage("在图片中选择新区块的左上角点，按Esc键取消。")

    def add_block(self, x=0, y=0):
        # add to the block list
        logging.debug("add block at {}, {}".format(x, y))
        block_list = self.block_list
        row = block_list.currentRow()
        logging.debug("insert row {}".format(row))
        if row < 0:
            row = block_list.rowCount()
        else:
            row += 1
        logging.debug("insert row {}".format(row))
        block_list.insertRow(row)
        if block_list.columnCount() < 2:
            block_list.insertColumn(0)
            block_list.insertColumn(1)
        block_list.setItem(row, 0, QtGui.QTableWidgetItem(str(x)))
        block_list.setItem(row, 1, QtGui.QTableWidgetItem(str(y)))
        block_list.setCurrentCell(row, 0)
        self.statusBar().showMessage("加入块({}, {})".format(x, y))

    def move_up_block(self):
        if not self.template:
            logging.debug("template not loaded")
            return
        block_list = self.block_list
        idx = block_list.selectedIndexes()
        if idx:
            row = idx[0].row()
            logging.debug('move row #%d up' % row)
            prev_row = row - 1
            if prev_row >= 0:
                self.row_swap(row, prev_row)
                block_list.setCurrentCell(prev_row, 0)
                block_list.emit(QtCore.SIGNAL('tableChanged()'))

    def move_down_block(self):
        if not self.template:
            logging.debug("template not loaded")
            return
        block_list = self.block_list
        idx = block_list.selectedIndexes()
        if idx:
            row = idx[0].row()
            logging.debug('move row #%d down' % row)
            next_row = row + 1
            if next_row < block_list.rowCount():
                self.row_swap(row, next_row)
                block_list.setCurrentCell(next_row, 0)
                block_list.emit(QtCore.SIGNAL('tableChanged()'))

    def row_swap(self, row, prev_row):
        block_list = self.block_list
        for col in range(block_list.columnCount()):
            a, b = block_list.item(row, col).text(), block_list.item(prev_row, col).text()
            block_list.setItem(row, col, QtGui.QTableWidgetItem(b))
            block_list.setItem(prev_row, col, QtGui.QTableWidgetItem(a))


class PathFinder(QtGui.QWidget):
    def __init__(self, parent=None, hint="", filter=None):
        super().__init__(parent)
        self.myicons = MyIcons()

        hbox = QtGui.QHBoxLayout()
        self.path = ""
        self.filter = filter
        self.hint = hint
        self.path_entry = QtGui.QLineEdit()
        self.browse = QtGui.QPushButton()
        self.browse.setIcon(self.myicons.icon_search)
        self.browse.setToolTip("浏览")
        self.load = QtGui.QPushButton()
        self.load.setIcon(self.myicons.icon_open)
        self.load.setToolTip("载入")
        hbox.addWidget(self.path_entry)
        hbox.addWidget(self.browse)
        hbox.addWidget(self.load)
        self.setLayout(hbox)
        self.set_hint()
        self.connect_event()

    def connect_event(self):
        self.connect(self.browse, QtCore.SIGNAL("clicked()"), self.find_file)
        self.connect(self.path_entry, QtCore.SIGNAL("textChanged(QString)"), self.update_path)

    def disconnect_event(self):
        self.disconnect(self.browse, QtCore.SIGNAL("clicked()"), self.find_file)
        self.disconnect(self.path_entry, QtCore.SIGNAL("textChanged(QString)"), self.update_path)

    def set_hint(self):
        self.path_entry.setText(self.hint)

    def show_path(self):
        self.path_entry.setText(self.path)

    def update_path(self):
        path = self.path_entry.text()
        if path:
            self.path = path
        else:
            self.path = ""
            self.disconnect_event()
            self.set_hint()
            self.connect_event()

    def find_file(self):
        path = QtGui.QFileDialog.getOpenFileName(filter=self.filter)
        if path:
            self.path = path
            self.show_path()
            return path


app = QtGui.QApplication(sys.argv)
gui = TemplateManagerMain()
gui.show()
app.exec_()
